// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DeviceConfiguration.cs" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace Microsoft.Robotics.Hardware.EV4
{
    using System;
    using System.Runtime.Serialization;

    using Microsoft.Robotics.Hardware.DAC;
    using Microsoft.Robotics.Numerics;

    /// <summary>
    /// Handles configuration data for EV4 robot
    /// </summary>
    [DataContract]
    public class DeviceConfiguration
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="DeviceConfiguration" /> class.
        /// </summary>
        public DeviceConfiguration()
        {
            this.Accelerometer = new AccelerometerConfiguration();
            this.Battery = new BatteryConfiguration();
            this.Button = new ButtonConfiguration();
            this.Compass = new CompassConfiguration();
            this.Drive = new DifferentialDriveConfiguration();
            this.Encoder1 = new EncoderConfiguration();
            this.Encoder2 = new EncoderConfiguration();
            this.Encoder3 = new EncoderConfiguration();
            this.Encoder4 = new EncoderConfiguration();
            this.LeftFan = new FanConfiguration();
            this.RightFan = new FanConfiguration();
            this.Gyroscope = new GyroscopeConfiguration();
            this.HumiditySensor = new HumidityConfiguration();
            this.IRFrontLeft = new ProximityConfiguration();
            this.IRFrontCenter = new ProximityConfiguration();
            this.IRFrontRight = new ProximityConfiguration();
            this.IRRearRight = new ProximityConfiguration();
            this.CliffFrontLeft = new ProximityConfiguration();
            this.CliffFrontCenter = new ProximityConfiguration();
            this.CliffFrontRight = new ProximityConfiguration();
            this.CliffRearLeft = new ProximityConfiguration();
            this.CliffRearRight = new ProximityConfiguration();
            this.Turret = new JointConfiguration();
            this.Hood = new JointConfiguration();
            this.Power = new PowerConfiguration();
            this.Projector = new ProjectorConfiguration();
            this.SonarLeft = new SonarConfiguration();
            this.SonarRight = new SonarConfiguration();
            this.BatteryTemperature = new TemperatureConfiguration();
            this.TemperatureInternal = new TemperatureConfiguration();
            this.TemperatureExternal = new TemperatureConfiguration();
            this.TemperatureProjectorRed = new TemperatureConfiguration();
            this.TemperatureProjectorGreen = new TemperatureConfiguration();
            this.TemperatureProjectorBlue = new TemperatureConfiguration();
        }

        /// <summary>
        /// Gets or sets distance between the drive wheels in meters
        /// </summary>
        [DataMember]
        public double WheelBaseInMeters { get; set; }

        /// <summary>
        /// Gets or sets diameter of the wheels in meters
        /// </summary>
        [DataMember]
        public double WheelDiameterInMeters { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of Accelerometer
        /// </summary>
        [DataMember]
        public AccelerometerConfiguration Accelerometer { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of the battery
        /// </summary>
        [DataMember]
        public BatteryConfiguration Battery { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of the button sensor
        /// </summary>
        [DataMember]
        public ButtonConfiguration Button { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of the compass
        /// </summary>
        [DataMember]
        public CompassConfiguration Compass { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of the drive
        /// </summary>
        [DataMember]
        public DifferentialDriveConfiguration Drive { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of encoder 1
        /// </summary>
        [DataMember]
        public EncoderConfiguration Encoder1 { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of encoder 2
        /// </summary>
        [DataMember]
        public EncoderConfiguration Encoder2 { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of encoder 3
        /// </summary>
        [DataMember]
        public EncoderConfiguration Encoder3 { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of encoder 4
        /// </summary>
        [DataMember]
        public EncoderConfiguration Encoder4 { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of a left fan
        /// </summary>
        [DataMember]
        public FanConfiguration LeftFan { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of a right fan
        /// </summary>
        [DataMember]
        public FanConfiguration RightFan { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of a gyroscope
        /// </summary>
        [DataMember]
        public GyroscopeConfiguration Gyroscope { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of the humidity sensor
        /// </summary>
        [DataMember]
        public HumidityConfiguration HumiditySensor { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of a proximity sensor
        /// </summary>
        [DataMember]
        public ProximityConfiguration IRFrontLeft { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of a proximity sensor
        /// </summary>
        [DataMember]
        public ProximityConfiguration IRFrontCenter { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of a proximity sensor
        /// </summary>
        [DataMember]
        public ProximityConfiguration IRFrontRight { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of a proximity sensor
        /// </summary>
        [DataMember]
        public ProximityConfiguration IRRearRight { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of a cliff sensor
        /// </summary>
        [DataMember]
        public ProximityConfiguration CliffFrontLeft { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of a cliff sensor
        /// </summary>
        [DataMember]
        public ProximityConfiguration CliffFrontCenter { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of a cliff sensor
        /// </summary>
        [DataMember]
        public ProximityConfiguration CliffFrontRight { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of a cliff sensor
        /// </summary>
        [DataMember]
        public ProximityConfiguration CliffRearLeft { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of a cliff sensor
        /// </summary>
        [DataMember]
        public ProximityConfiguration CliffRearRight { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of the Turret joint
        /// </summary>
        [DataMember]
        public JointConfiguration Turret { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of the Hood joint
        /// </summary>
        [DataMember]
        public JointConfiguration Hood { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of the Power supply
        /// </summary>
        [DataMember]
        public PowerConfiguration Power { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of the Projector
        /// </summary>
        [DataMember]
        public ProjectorConfiguration Projector { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of the Left Sonar
        /// </summary>
        [DataMember]
        public SonarConfiguration SonarLeft { get; set; }

        /// <summary>
        /// Gets or sets the DeviceConfiguration of the Right Sonar
        /// </summary>
        [DataMember]
        public SonarConfiguration SonarRight { get; set; }

        /// <summary>
        /// Gets or sets the TemperatureConfiguration of the battery temp sensor
        /// </summary>
        [DataMember]
        public TemperatureConfiguration BatteryTemperature { get; set; }

        /// <summary>
        /// Gets or sets the TemperatureConfiguration of the internal temp sensor
        /// </summary>
        [DataMember]
        public TemperatureConfiguration TemperatureInternal { get; set; }

        /// <summary>
        /// Gets or sets the TemperatureConfiguration of external temp sensor
        /// </summary>
        [DataMember]
        public TemperatureConfiguration TemperatureExternal { get; set; }

        /// <summary>
        /// Gets or sets the TemperatureConfiguration of the red channel of the projector
        /// </summary>
        [DataMember]
        public TemperatureConfiguration TemperatureProjectorRed { get; set; }

        /// <summary>
        /// Gets or sets the TemperatureConfiguration of the green channel of the projector
        /// </summary>
        [DataMember]
        public TemperatureConfiguration TemperatureProjectorGreen { get; set; }

        /// <summary>
        /// Gets or sets the TemperatureConfiguration of the blue channel of the projector
        /// </summary>
        [DataMember]
        public TemperatureConfiguration TemperatureProjectorBlue { get; set; }

        /// <summary>
        /// Base class for all device configuration states
        /// </summary>
        [Serializable]
        public class StaticDeviceInfo
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="StaticDeviceInfo" /> class.
            /// </summary>
            public StaticDeviceInfo()
            {
                this.Model = "Default";
                this.Comment = "Enter device name or description";
                this.Pose = new Pose(new Vector3(0, 0, 0));
            }

            /// <summary>
            /// Gets or sets a comment field
            /// </summary>
            public string Comment { get; set; }

            /// <summary>
            /// Gets or sets the device identifier
            /// </summary>
            public int DeviceId { get; set; }

            /// <summary>
            /// Gets or sets the device model information
            /// </summary>
            public string Model { get; set; }

            /// <summary>
            /// Gets or sets the type of the device the sensor is related
            /// </summary>
            public DeviceType DeviceType { get; set; }

            /// <summary>
            /// Gets or sets the pose for the device/sensor
            /// </summary>
            public Pose Pose { get; set; }

            /// <summary>
            /// Converts machine native to milliseconds
            /// </summary>
            /// <param name="native">10s of milliseconds</param>
            /// <returns>Timestamp as milliseconds</returns>
            internal int NativeToMilliseconds(long native)
            {
                return (int)(native * 10);
            }
        }

        /// <summary>
        /// Handles configuration of Accelerometer
        /// </summary>
        [Serializable]
        public class AccelerometerConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Generates and fills an AccelerometerConfiguration
            /// </summary>
            /// <param name="accelerationInG">The acceleration</param>
            /// <param name="nativeDeviceTimestamp">The timestamp in native format</param>
            /// <returns>A filled out state</returns>
            public AccelerometerState Generate(Vector3 accelerationInG, long nativeDeviceTimestamp)
            {
                return new AccelerometerState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        AccelerationInG = accelerationInG, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                    };
            }
        }

        /// <summary>
        /// Handles configuration of Battery
        /// </summary>
        [Serializable]
        public class BatteryConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Generates and fills a BatteryConfiguration
            /// </summary>
            /// <param name="voltage">Voltage of batter</param>
            /// <param name="temperatureInDegreesCelsius">Temp in celsius</param>
            /// <param name="maxVoltage">Max voltage of battery</param>
            /// <param name="currentInAmps">Current in amps</param>
            /// <param name="batteryCharging">Whether battery is charging.</param>
            /// <param name="chargePercentage">Charge percentage</param>
            /// <param name="batteryStatus">Batter status</param>
            /// <param name="nativeDeviceTimestamp">The timestamp in native format</param>
            /// <returns>A filled out state</returns>
            public BatteryState Generate(
                double voltage, 
                double temperatureInDegreesCelsius, 
                double maxVoltage, 
                double currentInAmps, 
                bool batteryCharging,
                double chargePercentage, 
                int batteryStatus, 
                long nativeDeviceTimestamp)
            {
                return new BatteryState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                        Voltage = voltage, 
                        TemperatureInDegreesCelsius = temperatureInDegreesCelsius, 
                        MaxVoltage = maxVoltage, 
                        CurrentInAmps = currentInAmps, 
                        BatteryCharging = batteryCharging,
                        ChargePercentage = chargePercentage, 
                        BatteryStatus = batteryStatus, 
                    };
            }
        }

        /// <summary>
        /// Handles configuration of Button
        /// </summary>
        [Serializable]
        public class ButtonConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Generates and fills a ButtonConfiguration
            /// </summary>
            /// <param name="isEngaged">True if engaged</param>
            /// <param name="nativeDeviceTimestamp">The timestamp in native format</param>
            /// <returns>A filled out state</returns>
            public ButtonState Generate(bool isEngaged, long nativeDeviceTimestamp)
            {
                return new ButtonState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                        IsEngaged = isEngaged, 
                    };
            }
        }

        /// <summary>
        /// Handles configuration of Compass
        /// </summary>
        [Serializable]
        public class CompassConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Generates and fills a CompassConfiguration
            /// </summary>
            /// <param name="magneticFieldInGauss">The magnetic field</param>
            /// <param name="compassHeading">The heading</param>
            /// <param name="nativeDeviceTimestamp">The timestamp in native format</param>
            /// <returns>A filled out state</returns>
            public CompassState Generate(
                Vector3 magneticFieldInGauss, double compassHeading, long nativeDeviceTimestamp)
            {
                return new CompassState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                        MagneticFieldInGauss = magneticFieldInGauss, 
                        CompassHeading = compassHeading
                    };
            }
        }

        /// <summary>
        /// Handles configuration of Drive
        /// </summary>
        [Serializable]
        public class DifferentialDriveConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Generates and fills a DifferentialDriveConfiguration
            /// </summary>
            /// <param name="rightWheelVelocityInMetersPerSecond">The right wheel velocity</param>
            /// <param name="rightWheelPositionInMM">The right wheel position</param>
            /// <param name="rightWheelAccelerationInMetersPerSecondSquared">The right wheel acceleration</param>
            /// <param name="leftWheelVelocityInMetersPerSecond">The left wheel velocity</param>
            /// <param name="leftWheelPositionInMM">The left wheel position</param>
            /// <param name="leftWheelAccelerationInMetersPerSecondSquared">The left wheel acceleration</param>
            /// <param name="nativeDeviceTimestamp">The timestamp in native format</param>
            /// <param name="wheelBaseInMeters">Device wheelbase in meters</param>
            /// <returns>A filled out state</returns>
            public DifferentialDriveState Generate(
                double rightWheelVelocityInMetersPerSecond, 
                double rightWheelPositionInMM, 
                double rightWheelAccelerationInMetersPerSecondSquared, 
                double leftWheelVelocityInMetersPerSecond, 
                double leftWheelPositionInMM, 
                double leftWheelAccelerationInMetersPerSecondSquared, 
                long nativeDeviceTimestamp,
                double wheelBaseInMeters)
            {
                return new DifferentialDriveState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                        RightWheelVelocityInMetersPerSecond = rightWheelVelocityInMetersPerSecond, 
                        RightWheelPositionInMeters = rightWheelPositionInMM / 1000.0, 
                        RightWheelAccelerationInMetersPerSecondSquared = rightWheelAccelerationInMetersPerSecondSquared, 
                        LeftWheelVelocityInMetersPerSecond = leftWheelVelocityInMetersPerSecond, 
                        LeftWheelPositionInMeters = leftWheelPositionInMM / 1000.0, 
                        LeftWheelAccelerationInMetersPerSecondSquared = leftWheelAccelerationInMetersPerSecondSquared,
                        WheelBaseInMeters = wheelBaseInMeters,
                    };
            }
        }

        /// <summary>
        /// Handles configuration of Encoder
        /// </summary>
        [Serializable]
        public class EncoderConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Generates and fills a EncoderConfiguration
            /// </summary>
            /// <param name="countsPerRevolution">The counts per revolution</param>
            /// <param name="count">The count</param>
            /// <param name="nativeDeviceTimestamp">The timestamp in native format</param>
            /// <returns>A filled out state</returns>
            public EncoderState Generate(int countsPerRevolution, int count, long nativeDeviceTimestamp)
            {
                return new EncoderState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                        CountsPerRevolution = countsPerRevolution, 
                        Count = count, 
                    };
            }
        }

        /// <summary>
        /// Handles configuration of Fan
        /// </summary>
        [Serializable]
        public class FanConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Generates and fills a FanConfiguration
            /// </summary>
            /// <param name="voltageInPercentage">Percent of voltage used</param>
            /// <param name="rotationsPerMinute">The RPM</param>
            /// <param name="nativeDeviceTimestamp">The timestamp in native format</param>
            /// <returns>A filled out state</returns>
            public FanState Generate(double voltageInPercentage, int rotationsPerMinute, long nativeDeviceTimestamp)
            {
                return new FanState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                        VoltageInPercentage = voltageInPercentage, 
                        RotationsPerMinute = rotationsPerMinute, 
                    };
            }
        }

        /// <summary>
        /// Handles configuration of Gyroscope
        /// </summary>
        [Serializable]
        public class GyroscopeConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Generates and fills a GyroscopeConfiguration
            /// </summary>
            /// <param name="rotationalVelocityInRadiansPerSecond">The rotational velocity in radians per second</param>
            /// <param name="nativeDeviceTimestamp">The timestamp in native format</param>
            /// <returns>A filled out state</returns>
            public GyroscopeState Generate(Vector3 rotationalVelocityInRadiansPerSecond, long nativeDeviceTimestamp)
            {
                return new GyroscopeState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                        RotationalVelocityInRadiansPerSecond = rotationalVelocityInRadiansPerSecond, 
                    };
            }
        }

        /// <summary>
        /// Handles configuration of Humidity Sensor
        /// </summary>
        [Serializable]
        public class HumidityConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Generates and fills a HumidityConfiguration
            /// </summary>
            /// <param name="relativeHumidityInPercentage">Percentage of humidity</param>
            /// <param name="nativeDeviceTimestamp">The timestamp in native format</param>
            /// <returns>A filled out state</returns>
            public HumidityState Generate(double relativeHumidityInPercentage, long nativeDeviceTimestamp)
            {
                return new HumidityState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                        RelativeHumidityInPercentage = relativeHumidityInPercentage, 
                    };
            }
        }

        /// <summary>
        /// Handles configuration of Prox Sensor
        /// </summary>
        [Serializable]
        public class ProximityConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Gets or sets the minimum distance at which sensor will detect collision
            /// </summary>
            [DataMember]
            public double MinimumDistanceInMeters { get; set; }

            /// <summary>
            /// Gets or sets the maximum distance at which sensor will detect collision
            /// </summary>
            [DataMember]
            public double MaximumDistanceInMeters { get; set; }

            /// <summary>
            /// Gets or sets the resolution of the sensor
            /// </summary>
            [DataMember]
            public double DistanceResolutionInMeters { get; set; }

            /// <summary>
            /// Generates and fills a ProximityConfiguration
            /// </summary>
            /// <param name="distanceInMeters">Distance in meters</param>
            /// <param name="nativeDeviceTimestamp">The timestamp in native format</param>
            /// <returns>A filled out state</returns>
            public IRProximityState Generate(double distanceInMeters, long nativeDeviceTimestamp)
            {
                return new IRProximityState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                        MinimumDistanceInMeters = this.MinimumDistanceInMeters, 
                        MaximumDistanceInMeters = this.MaximumDistanceInMeters, 
                        DistanceResolutionInMeters = this.DistanceResolutionInMeters, 
                        DistanceInMeters = distanceInMeters, 
                    };
            }
        }

        /// <summary>
        /// Handles configuration of Joints
        /// </summary>
        [Serializable]
        public class JointConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Gets or sets min position in radians
            /// </summary>
            [DataMember]
            public double MinPositionInRadians { get; set; }

            /// <summary>
            /// Gets or sets max position in radians
            /// </summary>
            [DataMember]
            public double MaxPositionInRadians { get; set; }

            /// <summary>
            /// Generates and fills a JointConfiguration
            /// </summary>
            /// <param name="positionPrecisionInRadians">Precision of joint</param>
            /// <param name="positionInRadians">Position in radians</param>
            /// <param name="currentVelocityInRadiansPerSecond">Current velocity</param>
            /// <param name="currentAccelerationInRadiansPerSecondSquared">Current acceleration</param>
            /// <param name="nativeDeviceTimestamp">The timestamp in native format</param>
            /// <returns>A filled out state</returns>
            public JointState Generate(
                double positionPrecisionInRadians, 
                double positionInRadians, 
                double currentVelocityInRadiansPerSecond, 
                double currentAccelerationInRadiansPerSecondSquared, 
                long nativeDeviceTimestamp)
            {
                return new JointState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                        PositionPrecisionInRadians = positionPrecisionInRadians, 
                        PositionInRadians = positionInRadians, 
                        MinPositionInRadians = this.MinPositionInRadians, 
                        MaxPositionInRadians = this.MaxPositionInRadians, 
                        CurrentVelocityInRadiansPerSecond = currentVelocityInRadiansPerSecond, 
                        CurrentAccelerationInRadiansPerSecondSquared = currentAccelerationInRadiansPerSecondSquared, 
                    };
            }
        }

        /// <summary>
        /// Handles configuration of Power Supply
        /// </summary>
        [Serializable]
        public class PowerConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Generates and fills a PowerConfiguration
            /// </summary>
            /// <param name="state">The power state</param>
            /// <param name="nativeDeviceTimestamp">The timestamp in native format</param>
            /// <returns>A filled out state</returns>
            public PowerState Generate(int state, long nativeDeviceTimestamp)
            {
                return new PowerState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                        State = state, 
                    };
            }
        }

        /// <summary>
        /// Handles configuration of Projector
        /// </summary>
        [Serializable]
        public class ProjectorConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Generates and fills a ProjectorConfiguration
            /// </summary>
            /// <param name="verticalInversion">The vertical inversion</param>
            /// <param name="horizontialInversion">The horizontal inversion</param>
            /// <param name="focalLengthOffset">The focal length offset</param>
            /// <param name="brightness">The brightness</param>
            /// <param name="nativeDeviceTimestamp">The timestamp in native format</param>
            /// <returns>A filled out state</returns>
            public ProjectorState Generate(
                bool verticalInversion, 
                bool horizontialInversion, 
                short focalLengthOffset, 
                short brightness, 
                long nativeDeviceTimestamp)
            {
                return new ProjectorState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                        VerticalInversion = verticalInversion, 
                        HorizontialInversion = horizontialInversion, 
                        FocalLengthOffset = focalLengthOffset, 
                        Brightness = brightness, 
                    };
            }
        }

        /// <summary>
        /// Handles configuration of Sonar Sensor
        /// </summary>
        [Serializable]
        public class SonarConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Gets or sets the minimum detection range of the sonar
            /// </summary>
            public double MinimumDistanceInMeters { get; set; }

            /// <summary>
            /// Gets or sets the maximum detection range of the sonar
            /// </summary>
            public double MaximumDistanceInMeters { get; set; }

            /// <summary>
            /// Gets or sets the precision of the sonar
            /// </summary>
            public double DistancePrecisionInMeters { get; set; }

            /// <summary>
            /// Generates and fills a SonarConfiguration
            /// </summary>
            /// <param name="distanceInMeters">The distance in meters</param>
            /// <param name="nativeDeviceTimestamp">The timestamp in native format</param>
            /// <returns>A filled out state</returns>
            public SonarState Generate(double distanceInMeters, long nativeDeviceTimestamp)
            {
                return new SonarState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                        MinimumDistanceInMeters = this.MinimumDistanceInMeters, 
                        MaximumDistanceInMeters = this.MaximumDistanceInMeters, 
                        DistancePrecisionInMeters = this.DistancePrecisionInMeters, 
                        DistanceInMeters = distanceInMeters, 
                    };
            }
        }

        /// <summary>
        /// Handles configuration of Temperature Sensor
        /// </summary>
        [Serializable]
        public class TemperatureConfiguration : StaticDeviceInfo
        {
            /// <summary>
            /// Generates and fills a TemperatureConfiguration
            /// </summary>
            /// <param name="temperatureInDegreesCelsius">Degrees celsius</param>
            /// <param name="nativeDeviceTimestamp">Time stamp</param>
            /// <returns>A TemperatureState</returns>
            public TemperatureState Generate(double temperatureInDegreesCelsius, long nativeDeviceTimestamp)
            {
                return new TemperatureState()
                    {
                        DeviceId = this.DeviceId, 
                        DeviceType = this.DeviceType, 
                        Model = this.Model, 
                        Pose = this.Pose, 
                        TimestampInMilliseconds = this.NativeToMilliseconds(nativeDeviceTimestamp), 
                        TemperatureInDegreesCelsius = temperatureInDegreesCelsius, 
                    };
            }
        }
    }
}
